# encoding: utf-8
"""
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    created by lane.chang on '28/05/2024'
    comment: 用户意图相关逻辑
"""
import datetime

import asyncio
import ujson
from pydantic import Field
from laner.pydantic import BaseModel
from typing import Union

from project.lib.common import get_redis, logger
from project.middleware.chatgpt import ChatGPT
from project.lib.decorator import retry
from project.core.base import Element, UEnum
from project.core.log import Colors
from project.core.function import match_text
from project.model.basic import Role, Question
from project.model.chatgpt import Chat
from project.service.common import CommonService


class Intent(UEnum):
    """ 用户意图
    """
    REPORT = Element('report', '报告', desc='用户想查看睡眠报告', examples=['昨天的睡眠报告怎么样？', '我想看看18号的睡眠报告', '我最近睡得怎么样？'], rank=0)
    PRODUCT = Element('product', '产品', desc='推荐产品', examples=[], rank=1)
    SERVICE = Element('service', '服务', desc='推荐服务', examples=[], rank=1)
    FIXED_QA = Element('fixed_qa', '固定问答', desc='酒店预先设置的一些高频问答库', examples=[''], rank=0)
    VIDEO_LINK = Element('video_link', '发起视频', desc='用户希望发起视频通话', examples=['视频通话', '我要视频通话', '请连接专家'], rank=0)
    INFERENCE_USER_DATE = Element('inference_user_date', '推测日期', desc='推测用户说指定的日期', examples=[], rank=99)
    CARD_SERVER = Element('card_server', '固定问答', desc='用户希望看到一些卡片', examples=['现在都有哪些卡片？', '这些卡片是干什么用的？'], rank=0)


class Domain(UEnum):
    """ 话题领域
    """
    HOTEL_SERVICE = Element('hotel_service', '酒店服务')
    HEALTH_SERVICE = Element('health_service', '健康服务')
    SWITCH_STAFF = Element('switch_staff', '更换数字人')


class Product(BaseModel):
    """ 产品
    """
    id: Union[int, str] = Field(0, title='产品id')
    name: str = Field('', title='产品名称')
    introduction: str = Field('', title='产品介绍')


class Item(BaseModel):
    """
    """
    title: str = Field('', title='商品名称')
    price: float = Field(None, title='商品单价')
    detail: list = Field({}, title='商品详情')


class Service(BaseModel):
    """ 服务
    """
    id: Union[int, str] = Field(0, title='服务id')
    name: str = Field('', title='服务名称')
    introduction: str = Field('', title='服务介绍')
    items: list[Item] = Field([], title='商品列表')
    test: dict = Field([], title='')

    @property
    def ai_instruction(self) -> str:
        """ 详细介绍
        :return:
        """
        service_name = f'服务名称: {self.name}'

        items_tag = '${items}'
        service_instruction = ''
        if self.introduction:
            service_instruction = f'服务详情: {self.introduction}'

            if items_tag in service_instruction:
                item_titles = [v.title for v in self.items]
                service_instruction = service_instruction.replace(items_tag, '、'.join(item_titles))

        return service_name + ' ' + service_instruction


# 判断数字人结果缓存KEY
CHOOSE_STAFF = 'choose_staff'


class IntentService:
    """
    """

    @staticmethod
    def user_chat_memories_key(user_id):
        """ 用户对话记忆key
        :param user_id:
        :return:
        """
        return f'user_chat_memory_{user_id}'

    @staticmethod
    def add_user_chat(user_id, user_message, type='ask'):
        """
        :param user_id:
        :param user_message:
        :param type:
        :return:
        """
        rds = get_redis()
        key = IntentService.user_chat_memories_key(user_id)

        if type == 'ask':
            rds.rpush(key, f'用户说: "{user_message}"')
        else:
            rds.rpush(key, f'员工说: "{user_message}"')

    @staticmethod
    def get_user_chats(user_id, chat_count=2):
        """
        :param user_id:
        :param chat_count:
        :return:
        """
        rds = get_redis()
        key = IntentService.user_chat_memories_key(user_id)
        chats = rds.lrange(key, 0, -1) or []
        chats.reverse()

        contexts = []
        count = 0
        for chat in chats:
            if chat.startswith('用户'):
                contexts.append(chat)
                count += 1
                continue

            if count >= chat_count:
                break
            else:
                contexts.append(chat)

        return contexts

    @staticmethod
    def user_flow_memories_key(bot_id, user_id):
        """ 酒店服务对话记录
        :param bot_id:
        :param user_id:
        :return:
        """
        return f'user_flow_{bot_id}_{user_id}'

    @staticmethod
    def add_user_flow(bot_id, user_id, observation, thought, action):
        """
        :param bot_id:
        :param user_id:
        :param observation:
        :param thought:
        :param action:
        :return:
        """
        rds = get_redis()

        key = IntentService.user_flow_memories_key(bot_id, user_id)
        data = {'Observation': observation, 'Thought': thought, 'Action': action}
        rds.rpush(key, ujson.dumps(data))

    @staticmethod
    def clear_user_flow(bot_id, user_id):
        """
        :param bot_id:
        :param user_id:
        :return:
        """
        rds = get_redis()

        key = IntentService.user_flow_memories_key(bot_id, user_id)
        if rds.exists(key):
            rds.delete(key)

    @staticmethod
    def get_user_flow_memories(bot_id, user_id):
        """
        :param bot_id:
        :param user_id:
        :return:
        """
        rds = get_redis()

        key = IntentService.user_flow_memories_key(bot_id, user_id)
        data = rds.lrange(key, 0, -1) or []

        memories = []
        for idx, v in enumerate(data):
            d = ujson.loads(v)
            d['index'] = idx + 1
            memories.append(d)

        return memories

    @staticmethod
    # @func_log(title='推荐健康产品')
    async def recommend_health_product(user_message, products):
        """ 建议产品
        :param user_message:
        :param products:
        :return:
        """
        prompt = f"""
###Instruction
请根据用户的当前消息，请严格判断以下商品列表中是否存在可以推荐给用户的商品，如无非常对应的则不推荐。请严格按照输出格式要求输出，并输出你的推理过程。


#商品列表
{products}

#输出格式
用户对你说："...这里是用户对你说的话..." <reason>你的推理过程：...</reason><product_id>推荐的产品的id</product_id><message>你对用户说的话</message>

#输出样例
# 输出样例
用户对你说："今天开心吗？" <reason>用户在询问今天的情绪，没有提到任何具体需求，无法推断出需要什么商品。</reason><product_id>None</product_id>
用户对你说："如何改善睡眠？" <reason>用户提到了改善睡眠质量，可以考虑推荐新中式草本茶中的熬夜茶，适用于健忘嗜睡、疲乏无力的情况。</reason><product_id>None</product_id><message>您可以考虑尝试我们的新中式草本茶中的熬夜茶，有助于改善睡眠质量。</message>

###Output
用户对你说："{user_message}" 
        """
        chat_gpt = ChatGPT()
        res = await chat_gpt.json_loads_content(user_text=prompt, tags=('reason', 'product_id'))
        product_id = res.pop('product_id', 'None')
        # 无产品生成
        if product_id == 'None':
            return
        res['id'] = product_id
        res['intent'] = str(Intent.PRODUCT.value)
        res['domain'] = str(Domain.HEALTH_SERVICE.value)

        return res

    @staticmethod
    # @func_log(title='推荐健康服务')
    async def recommend_health_service(user_message, services):
        """ 建议服务
        :param user_message:
        :param services:
        :return:
        """
        chat_gpt = ChatGPT()
        prompt = f"""
###Instruction
请根据用户的当前消息，请严格判断以下服务列表中是否存在可以推荐给用户的服务，如无非常对应的则不推荐。请严格按照输出格式要求输出，并输出你的推理过程。

#服务列表
{services}

#输出格式
用户对你说："...这里是用户对你说的话..." <reason>你的推理过程：...</reason><product_id>推荐服务的id</service_id><message>你对用户说的话</message>

#输出样例
# 输出样例
用户对你说："今天开心吗？" <reason>用户在询问今天的情绪，没有提到任何具体需求，无法推断出需要什么服务。</reason><service_id>None</service_id> 
用户对你说："如何改善睡眠？" <reason>用户提到了想要改善睡眠质量，可以推测用户可能需要放松和舒缓身心来帮助入眠。</reason><service_id>None</service_id><message>您可以尝试我们的【深度舒压】全身精油推拿（热灸）+泡脚+茶饮服务，可以帮助您放松身心，改善睡眠质量。</message>

###Output
用户对你说："{user_message}" 
            """
        res = await chat_gpt.json_loads_content(user_text=prompt, tags=('reason', 'service_id'))
        service_id = res.pop('service_id', 'None')
        # 无产品生成
        if service_id == 'None':
            return
        res['id'] = service_id
        res['intent'] = str(Intent.SERVICE.value)
        res['domain'] = str(Domain.HEALTH_SERVICE.value)

        return res

    @staticmethod
    # @func_log(title='推荐酒店服务')
    @retry(count=3)
    async def recommend_hotel_service(bot_id, user_id, user_message, services):
        """ 推荐酒店服务
        :param bot_id:
        :param user_id:
        :param user_message:
        :param services:
        :return:
        """
        user_memories = IntentService.get_user_flow_memories(bot_id, user_id)
        history = f""""""
        for fm in user_memories:
            history += fm['Observation'] + '\n' + fm['Thought'] + '\n' + fm['Action'] + '\n'
        cur_observation = 'Observation ' + str(len(user_memories) + 1) + ": 用户说:\"" + user_message + "\""
        history += cur_observation

        prompt = f"""
###Instruction
假设你是一名酒店客房服务员。酒店能提供的服务，都在以下服务列表中。你需要根据与用户的对话，从服务列表中推荐一项能解决用户需求的服务给用户。
当你确定推荐哪一项服务后，会对该候选服务进行介绍，并在屏幕上显示该服务的功能入口。
            
Solve this 'service recommendation' task with interleaving Thought, Action, and Observation steps. Thought can reason about the current situation, and Action can be two types: 
(1) 追问需求，Ask [<service>服务名称</service>][<message>消息内容</message>]，用户的对话信息与某个候选服务接近，但还不能十分确定该服务与用户的需求匹配，或是能解决用户的问题，因此对用户发起追问，进一步确定是否候选服务是否与用户需求匹配。
(2) 介绍服务，Introduce [<service>服务名称</service>][<message>消息内容</message>]，确认某个候选服务符合用户的需求后，对该候选服务进行介绍，提示用户点击屏幕中的服务入口来使用服务。
注意每次只生成一个 Thought 和 一个 Action ，两者要对应，要知行合一。

Here are some examples.
#有服务对应，追问详情后，成功推荐服务的例子：
    Observation 1: 用户说:"给我送个东西。"
    Thought 1: 服务列表中是存在'物品配送'这个服务的，但是不知道用户需要的物品是否在配送范围内，因此我需要进一步询问用户用户需要的是什么？
    Action 1: Ask [<service>物品配送</service>][<message>请问您需要为您配送什么物品？</message>]
    Observation 2: 用户说:"我需要一条毛巾"
    Thought 2: 在'物品配送'这个服务中，毛巾属于可以配送的物品范围，因此确定可以推荐'物品配送'这项服务给用户。
    Action 2: Introduce [<service>物品配送</service>][<message>您可以通过下方物品配送的服务链接来完成物品配送</message>]
#疑似有服务对应，追问后，实际无有服务对应，转人工的例子：
    Observation 1: 用户说:"给我送个东西。"
    Thought 1: 服务列表中是存在'物品配送'这个服务的，但是不知道用户需要的物品是否在配送范围内，因此我需要进一步询问用户用户需要的是什么？
    Action 1: Ask [<service>物品配送</service>][<message>请问您需要为您配送什么物品？</message>]
    Observation 2: 用户说:"我需要一个毛绒玩具"
    Thought 2: 在'物品配送'这个服务中，毛绒玩具不属于可以配送的物品范围，所以'物品配送'这项服务无法满足用户的需求，因此推荐'人工服务'这项服务给用户。
    Action 2: Introduce [<service>人工服务</service>][<message>抱歉，您提到的物品无法直接为您配送，您看是否需要为您联系前台，通过点击下方按钮您可以直接与前台通话。</message>]
#有服务对应，非常匹配需求，不用追问需求详情，直接推荐服务的例子：
    Observation 1: 用户说:"给我送把剃须刀。"
    Thought 1: 服务列表中是存在'物品配送'这个服务的，并且剃须刀属于配送范围。可以确定'物品配送'服务可以满足用户需求，因此确定可以推荐'物品配送'这项服务给用户。
    Action 1: Introduce [<service>物品配送</service>][<message>您需要的剃须刀可以通过下方的物品配送服务链接来完成配送</message>]
#无服务对应，转人工的例子：
    Observation 1: 用户说:"我想订机票"
    Thought 1: 服务列表中没有服务与用户的订机票的需求对应，因此推荐'人工服务'这项服务给用户。
    Action 1: Introduce [<service>人工服务</service>][<message>很抱歉，我暂时还不能完成你交代的任务，您看是否需要为您联系前台，通过下方链接您可以直接与前台通话。</message>]

#服务列表
{services}

###Output
{history}
        """
        chat_gpt = ChatGPT()
        response = await chat_gpt.llm_async(user_text=prompt)

        new_flow_memory = {}
        formatted_response = response.split('\n')
        for l in formatted_response:
            if l.startswith('Thought'):
                new_flow_memory['Thought'] = l
            if l.startswith('Action'):
                new_flow_memory['Action'] = l

        action = 'introduce' if 'Introduce' in response else 'ask'
        if action == 'introduce':
            IntentService.clear_user_flow(bot_id, user_id)
        else:
            IntentService.add_user_flow(bot_id, user_id, observation=cur_observation, thought=new_flow_memory['Thought'], action=new_flow_memory['Action'])

        message = match_text(response, 'message')
        service = match_text(response, 'service')
        result = {'domain': 'hotel_service', 'intent': 'service', 'service_name': service, 'action': action, 'response': message}

        return result

    @staticmethod
    # @func_log(title='固定问答')
    async def kbqa_handler(user_message, questions, domain):
        """ 由于预设了FAQ优先级最高，目前是严格匹配模式。
        :param user_message:
        :param questions: 问题列表
        :param domain:
        :return:
        """
        prompt = f"""
###Instruction
酒店预先设置了一些高频问答库，FAQ，当前你接收到了用户的一条消息，请严格根据知识库的信息回答用户，当用户说的话或提问与预先设置的FAQ库或知识库的内容都不相关时，你必须回复'None'
请按输出格式返回并输出你的推理过程。

#酒店预先设置的高频问答库
{questions}

#输出格式
用户对你说："...这里是用户对你说的话..." <reason>你的推理过程：...</reason> <answer>你回复用户的话</answer> 

#输出样例
用户对你说："wifi密码多少？" <reason>你的推理过程：用户询问的是WiFi密码，这与预先设置的FAQ库中的第2条信息匹配。</reason> <answer>WiFi密码是88888888。</answer>
用户对你说："你吃了吗？"<reason>你的推理过程：这与预先设置的FAQ库中的内容不匹配。所以我无法直接从FAQ库中找到答案。</reason> <answer>None</answer>
用户对你说："你是谁？"<reason>你的推理过程：用户对你的身份和能做的事情感到好奇，这与知识库中"自我介绍"匹配。</reason> <answer>你好，我是酒店管家Youdee，我能依赖床垫下的生命体征检测传感器晚上监测您的睡眠状况，为您生成睡眠报告。也能扫描您的面部为您做健康体检。同时您还可以通过我预定酒店餐食、让我送水、拖鞋等。</answer>
用户对你说："给我送杯水"<reason>你的推理过程：这与预先设置的FAQ库中的Q内容不匹配。所以我无法直接从FAQ库中找到答案。</reason> <answer>None</answer>
###Output
用户对你说："{user_message}"
        """
        try:
            chat_gpt = ChatGPT()
            res = await chat_gpt.json_loads_content(user_text=prompt, tags=('reason', 'answer'))
            answer = res.pop('answer', 'None')
            if answer == 'None':
                return
            res['domain'] = domain
            res['intent'] = str(Intent.FIXED_QA.value)
            res['response'] = answer

        except Exception as ex:
            raise ex

        return res

    @staticmethod
    # @func_log(title='健康服务意图')
    async def health_service_handler(user_message):
        """
        :param user_message:
        :return:
        """
        prompt = f"""
###Instruction
请判断以下用户当前消息符合以下哪一个意图？请按输出格式要求输出，并输出你的推理过程。

#意图列表
意图编号：video_link 意图内容：用户明确提到要视频通话。
意图编号：report 意图内容：用户要查看自己的睡眠报告。
意图编号：other 意图内容：其他意图。
    
#输出格式
用户对你说："...这里是用户对你说的话..." <reason>你的推理过程：...</reason><intent>意图编号</intent><message>你对用户说的话</message>

# 输出样例
用户对你说："我想和王医生视频" <reason> 用户明确提到要视频通话 </reason><intent>video_link</intent><message>您可以通过下方链接开启视频通话</message>
用户对你说："我想看看我的睡眠报告" <reason>用户明确提到要查看自己的睡眠报告</reason><intent>report</intent>
用户对你说："我昨天睡的怎么样？" <reason>可能用户在关心自己的睡眠数据，推出睡眠报告</reason><intent>report</intent>
用户对你说："晚饭吃了吗？" <reason>用户没有明确提到视频通话、睡眠报告或健康检测</reason><intent>4</intent> 

###Output
用户对你说："{user_message}" 
        """

        chat_gpt = ChatGPT()
        res = await chat_gpt.json_loads_content(user_text=prompt, tags=('reason', 'intent', 'message'))
        # 无意图返回
        if not res.get('intent') or res.get('intent') not in ['video_link', 'report']:
            return
        res['domain'] = str(Domain.HEALTH_SERVICE.value)
        res['response'] = res.pop('message', '')

        return res

    @staticmethod
    # @func_log(title='判断数字人')
    async def choose_staff(bot_id, user_id, user_message, hotel_services, health_questions):
        """ 选择工作人员
        :param bot_id:
        :param user_id:
        :param user_message:
        :param hotel_services:
        :param health_questions
        :return:
        """
        carena_message_list = [
            "我昨晚的睡眠中有出现呼吸暂停吗？睡眠报告能显示吗？", "晚上经常醒来是什么原因？", "睡眠不足对身体有哪些长期影响？", "有什么方法可以快速入睡？", "睡眠呼吸中止症有哪些症状？", "睡前吃哪些食物可以帮助睡眠？", "不宁腿综合症如何影响睡眠？", "嗜睡症和失眠症有什么区别？", "梦游症需要治疗吗？", "夜惊症和噩梦有什么区别？", "睡前运动是否有助于改善睡眠？", "为什么晚上喝太多水会影响睡眠？",
            "为什么建议睡前不要使用手机？", "睡前洗澡对睡眠有何影响？", "睡前听轻音乐是否有助于睡眠？", "为什么建议睡前不要吃得过饱？", "睡前阅读是否有助于放松？", "为什么建议睡前不要喝咖啡因饮料？", "睡前冥想有哪些好处？", "我昨天睡的怎么样", "看下我昨天的睡眠报告", "看下睡眠报告", "昨天睡眠报告有什么异常吗？", "昨晚睡觉有什么异常吗？", "看下6月20号的睡眠报告", "看下6月2号的睡眠报告",
            "看下6月22号的睡眠报告", "看下6月27号的睡眠报告", "看下7月10号的睡眠报告"
        ]

        prompt = f"""
###Instructin
请判断以下用户最新消息内容是否应该由王医生来处理？
注意，王医生只能回答#以下问题列表中的问题，以及#常见用户消息中的问题，还有视频通话请求，睡眠报告相关的, 其他问题都不由王医生处理。
请按输出格式输出，并输出你的推理过程。
Let's think step-by-step

#用户的最新消息
{user_message}

#王医生的介绍
王医生能回答以下问题：
{health_questions}

当用户希望进行视频通话相关，王医生能为用户接通视频通话。

#常见用户消息
另外以下用户消息也应该由王医生回答
{carena_message_list}

#输出格式： 
当判定应该由王医生处理时：<reason>你的推理过程...</reason><result>Yes</result>
当判定不应该由王医生处理时：<reason>你的推理过程...</reason><result>No</result>


here are some examples:
用户说："我最近总是失眠"：<reason>根据常见用户消息中的问题，失眠是王医生的擅长领域，因此王医生应该处理。</reason><result>Yes</result>
用户说："需要"：<reason>不确定用户的问题，不由王医生处理</reason><result>No</result>
用户说："想看一下6月1号的睡眠报告"：<reason>根据常见用户消息中的问题，用户想要查看睡眠报告，因此王医生应该处理。</reason><result>Yes</result>
用户说："我昨晚睡的怎么样"：<reason>根据常见用户消息中的问题，用户关心自己的睡眠质量，是王医生擅长的领域, 因此王医生应该处理。</reason><result>Yes</result>
用户说："呼吸用户说："呼吸暂停风险是什么"：<reason>根据常见用户消息中的问题，在睡眠中出现呼吸暂停是王医生的擅长领域，因此王医生应该处理。</reason><result>Yes</result>暂停风险是什么"：<reason>根据常见用户消息中的问题，在睡眠中出现呼吸暂停是王医生的擅长领域，因此王医生应该处理。</reason><result>Yes</result>

###Output
用户说："{user_message}"
        """

        chat_gpt = ChatGPT(temperature=0)
        result = await chat_gpt.json_loads_content(user_text=prompt, tags=('reason', 'result'))

        target_bot_id = Role.UDI.value.bot_id
        if result.get('result', '').strip().lower() == 'yes':
            target_bot_id = Role.CAREANA.value.bot_id

        # 不需要切换数字人
        if target_bot_id == bot_id:
            logger.info(f'user_message: {user_message} 推理结果: choose_staff: {result}', font_color=Colors.LIGHT_BLUE.value)
            return

        # 切换酒店服务管理人员
        if target_bot_id == Role.UDI.value.bot_id:
            response = '这个问题将由我的同事Youdee为您解答，请稍等'
        else:
            response = '请稍等，健康问题将由我的同事Careana为您解答'

        result['domain'] = 'switch_staff'
        result['intent'] = ''
        result['target_bot_id'] = target_bot_id
        result['response'] = response

        return result

    @staticmethod
    def add_choose_staff_cache(user_message, target_bot_id):
        """ 增加判断数字人缓存(只有切换的场景才会添加)
        :param user_message:
        :param target_bot_id:
        :return:
        """
        rds = get_redis()

        rds.hset(CHOOSE_STAFF, user_message, target_bot_id)

    @staticmethod
    def delete_choose_staff_cache(user_message):
        """ 增加判断数字人缓存(只有切换的场景才会添加)
        :param user_message:
        :return:
        """
        rds = get_redis()

        rds.hdel(CHOOSE_STAFF, user_message)

    @staticmethod
    def get_choose_staff_cache(user_message):
        """
        :param user_message:
        :return:
        """
        rds = get_redis()

        target_bot_id = rds.hget(CHOOSE_STAFF, user_message)

        return target_bot_id

    @staticmethod
    def merge_response(responses):
        """ 合并数字人话术
        :param responses:
        :return:
        """
        prompt = f"""
        ###Instruction
            请对以下回复列表中的回复内容进行融合，形成一段语义统一的回复，注意回复内容必须是简体中文。

            #回复列表：{responses}
        ###Ouput
        """

        chat_gpt = ChatGPT()
        res = chat_gpt.llm(user_text=prompt)

        return res

    @staticmethod
    def format_hotel_services(services):
        """
        :return:
        """
        # 对services进行加工
        formatted_services = []
        for v in services:
            formatted_services.append(v.ai_instruction)
        formatted_services.append('服务名称: 人工服务')

        return '\n'.join(formatted_services)

    @staticmethod
    def format_questions(questions):
        """
        :param questions:
        :return:
        """
        formatted_questions = []
        for idx, v in enumerate(questions):
            formatted_questions.append(f"""
                Q: {v.index}
                A: {v.content}
                """)
        return '\n'.join(formatted_questions)

    @staticmethod
    async def get_user_intent_all(bot_id: str,
                                  user_id: str,
                                  user_message: str,
                                  hotel_services: list[Service] = None,
                                  hotel_questions: list[Question] = None,
                                  health_questions: list[Question] = None,
                                  health_products: list[Product] = None,
                                  health_services: list[Service] = None,
                                  dev_id: str = '',
                                  session=None) -> list:
        """ 得到用户意图(全部)
        :param bot_id:
        :param user_id:
        :param user_message:
        :param hotel_services:
        :param hotel_questions:
        :param health_questions:
        :param health_products: list[Product] = None,
        :param health_services: list[Service] = None,
        :param dev_id: 设备id
        :param session:
        :return:
        """
        # 将接收到的信息持久化
        IntentService.add_user_chat(user_id=user_id, user_message=user_message)
        chat = None
        if session:
            chat = Chat.create_modify(session, dev_id=dev_id or 'dev01', bot_id=bot_id, user_id=user_id, user_message=user_message)

        tasks = list()
        # 酒店服务
        if hotel_services:
            tasks.append(asyncio.create_task(IntentService.recommend_hotel_service(bot_id, user_id, user_message, IntentService.format_hotel_services(hotel_services)), name='hotel_service'))

        # 酒店固定问答
        if hotel_questions:
            tasks.append(asyncio.create_task(IntentService.kbqa_handler(user_message, IntentService.format_questions(hotel_questions), str(Domain.HOTEL_SERVICE.value)), name='hotel_fix_qa'))

        # 健康服务意图
        tasks.append(asyncio.create_task(IntentService.health_service_handler(user_message), name='health_intent'))

        # 健康服务固定问答
        if health_questions:
            tasks.append(asyncio.create_task(IntentService.kbqa_handler(user_message, IntentService.format_questions(health_questions), str(Domain.HEALTH_SERVICE.value)), name='health_fix_qa'))

        # 产品
        if health_products:
            tasks.append(asyncio.create_task(IntentService.recommend_health_product(user_message, [v.dict() for v in health_products]), name='health_product'))

        # 服务
        if health_services:
            tasks.append(asyncio.create_task(IntentService.recommend_health_service(user_message, [v.dict() for v in health_services]), name='health_service'), )

        # 推测日期
        tasks.append(asyncio.create_task(CommonService.inference_user_date_async(user_message), name='report_date'))

        await asyncio.gather(*tasks)

        hotel_fix_qa = {}  # 酒店固定答案
        hotel_service = {}  # 酒店服务

        health_fix_qa = {}  # 健康固定答案
        health_intent = {}  # 视频连接 或 日报
        health_service = {}  # 健康服务
        health_product = {}  # 健康产品
        report_date = ''
        for task in tasks:

            task_name = task.get_name()
            task_result = task.result()
            # 推理结果
            logger.info(f'user_message: {user_message} 推理结果: task_name: {task_name}  task_result: {task_result}', font_color=Colors.LIGHT_BLUE.value)
            if not task_result:
                continue
            task_result.pop('reason', None)
            task_result.pop('gpt_logic', None)

            # 酒店固定问答
            if task.get_name() == 'hotel_fix_qa':
                hotel_fix_qa = task_result

            elif task.get_name() == 'hotel_service':

                # 过滤掉假的酒店服务
                hotel_service_names = [v.name for v in hotel_services]
                service_name = task_result.get('service_name')
                if service_name not in hotel_service_names and service_name != '人工服务':
                    continue
                # 继续追问, 将服务名清空
                if task_result.get('action') == 'ask':
                    task_result['service_name'] = ''
                hotel_service = task_result

            elif task_name == 'health_fix_qa':
                health_fix_qa = task_result

            elif task_name == 'health_intent':
                health_intent = task_result

            elif task_name == 'health_service':
                health_service = task_result

            elif task_name == 'health_product':
                health_product = task_result

            elif task_name == 'report_date':
                result = task_result.get('result', [])
                report_date = result[0] if result else datetime.datetime.now().strftime('%Y-%m-%d')

        # 如果是切换数字人，直接返回该意图
        intents_hotel = list()
        # 因为酒店服务有【人工服务】兜底，所以【人工服务】的概率很大，就会屏蔽掉命中【固定问答】的情况。
        # 如果有【固定问答】的情况，则将【人工服务】屏蔽掉
        if hotel_fix_qa:
            intents_hotel.append(hotel_fix_qa)

        if hotel_service:
            if hotel_fix_qa:
                if hotel_service.get('service_name') != '人工服务':
                    intents_hotel.append(hotel_service)
            else:
                intents_hotel.append(hotel_service)

        intents_health = list()
        if health_intent:
            intents_health.append(health_intent)
            # 给睡眠报告添加报告时间
            if health_intent.get('intent') == str(Intent.REPORT.value):
                health_intent['report_date'] = report_date

        if health_fix_qa and not health_intent:
            intents_health.append(health_fix_qa)

        if health_product:
            intents_health.append(health_product)

        if health_service:
            intents_health.append(health_service)

        intents = intents_health or intents_hotel

        # 合并数字人话术
        responses = [v.get('response') for v in intents if v.get('response') and v.get('response') != 'None']
        if len(responses) > 1:
            response = IntentService.merge_response(responses)
            # 将回复融合到第一个元素中
            for idx, intent in enumerate(intents):
                if idx == 0:
                    intent['response'] = response
                else:
                    intent['response'] = ''
        else:
            response = responses[0] if responses else ''

        # 工作人员回答
        if responses:
            chat.bot_message = response
            chat.detail = {'intents': intents}

            session.commit()

        return intents

    @staticmethod
    async def get_user_intent(bot_id: str,
                              user_id: str,
                              user_message: str,
                              hotel_services: list[Service] = None,
                              hotel_questions: list[Question] = None,
                              health_questions: list[Question] = None,
                              health_products: list[Product] = None,
                              health_services: list[Service] = None,
                              dev_id: str = '',
                              session=None) -> list:
        """ 得到用户的意图
        :param bot_id:
        :param user_id:
        :param user_message:
        :param hotel_services:
        :param hotel_questions:
        :param health_questions:
        :param health_products: list[Product] = None,
        :param health_services: list[Service] = None,
        :param dev_id: 设备id
        :param session:
        :return:
        """
        # 将接收到的信息持久化
        chat = Chat.create_modify(session, dev_id=dev_id or 'dev01', bot_id=bot_id, user_id=user_id, user_message=user_message)

        # 先判断缓存有没有
        target_bot_id = IntentService.get_choose_staff_cache(user_message)
        if target_bot_id and target_bot_id == bot_id:
            logger.info(f'user_message: {user_message} 此场景不需推理')
            IntentService.delete_choose_staff_cache(user_message)
        else:
            # 判断数字人
            result = await IntentService.choose_staff(bot_id, user_id, user_message, hotel_services=IntentService.format_hotel_services(hotel_services), health_questions=IntentService.format_questions(health_questions))
            if result:
                # 将此结果缓存
                IntentService.add_choose_staff_cache(user_message, result.get('target_bot_id'))

                logger.info(f'user_message: {user_message} 推理结果: choose_staff: {result}', font_color=Colors.LIGHT_BLUE.value)
                IntentService.add_user_chat(user_id, result.get('response'), type='answer')

                if chat:
                    chat.bot_message = result.get('response')
                    chat.detail = {'intents': [result]}

                    session.commit()

                return [result]

        tasks = list()
        # 酒店服务
        if bot_id == Role.UDI.value.bot_id:
            # 酒店服务
            if hotel_services:
                tasks.append(asyncio.create_task(IntentService.recommend_hotel_service(bot_id, user_id, user_message, IntentService.format_hotel_services(hotel_services)), name='hotel_service'))

            # 酒店固定问答
            if hotel_questions:
                tasks.append(asyncio.create_task(IntentService.kbqa_handler(user_message, IntentService.format_questions(hotel_questions), str(Domain.HOTEL_SERVICE.value)), name='hotel_fix_qa'))
        # 健康服务
        elif bot_id == Role.CAREANA.value.bot_id:
            # 健康服务意图
            tasks.append(asyncio.create_task(IntentService.health_service_handler(user_message), name='health_intent'))

            # 健康服务固定问答
            if health_questions:
                tasks.append(asyncio.create_task(IntentService.kbqa_handler(user_message, IntentService.format_questions(health_questions), str(Domain.HEALTH_SERVICE.value)), name='health_fix_qa'))

            # 产品
            if health_products:
                tasks.append(asyncio.create_task(IntentService.recommend_health_product(user_message, [v.dict() for v in health_products]), name='health_product'))

            # 服务
            if health_services:
                tasks.append(asyncio.create_task(IntentService.recommend_health_service(user_message, [v.dict() for v in health_services]), name='health_service'), )

            # 推测日期
            tasks.append(asyncio.create_task(CommonService.inference_user_date_async(user_message), name='report_date'))

        await asyncio.gather(*tasks)

        hotel_fix_qa = {}  # 酒店固定答案
        hotel_service = {}  # 酒店服务

        health_fix_qa = {}  # 健康固定答案
        health_intent = {}  # 视频连接 或 日报
        health_service = {}  # 健康服务
        health_product = {}  # 健康产品
        report_date = ''
        for task in tasks:

            task_name = task.get_name()
            task_result = task.result()
            # 推理结果
            logger.info(f'user_message: {user_message} 推理结果: task_name: {task_name}  task_result: {task_result}', font_color=Colors.LIGHT_BLUE.value)
            if not task_result:
                continue
            task_result.pop('reason', None)
            task_result.pop('gpt_logic', None)

            # 酒店固定问答
            if task.get_name() == 'hotel_fix_qa':
                hotel_fix_qa = task_result

            elif task.get_name() == 'hotel_service':

                # 过滤掉假的酒店服务
                hotel_service_names = [v.name for v in hotel_services]
                service_name = task_result.get('service_name')
                if service_name not in hotel_service_names and service_name != '人工服务':
                    continue
                # 继续追问, 将服务名清空
                if task_result.get('action') == 'ask':
                    task_result['service_name'] = ''
                hotel_service = task_result

            elif task_name == 'health_fix_qa':
                health_fix_qa = task_result

            elif task_name == 'health_intent':
                health_intent = task_result

            elif task_name == 'health_service':
                health_service = task_result

            elif task_name == 'health_product':
                health_product = task_result

            elif task_name == 'report_date':
                result = task_result.get('result', [])
                report_date = result[0] if result else datetime.datetime.now().strftime('%Y-%m-%d')

        # 如果是切换数字人，直接返回该意图
        intents = list()
        # 优优
        if bot_id == Role.UDI.value.bot_id:
            # 因为酒店服务有【人工服务】兜底，所以【人工服务】的概率很大，就会屏蔽掉命中【固定问答】的情况。
            # 如果有【固定问答】的情况，则将【人工服务】屏蔽掉
            if hotel_fix_qa:
                intents.append(hotel_fix_qa)

            if hotel_service:
                if hotel_fix_qa:
                    if hotel_service.get('service_name') != '人工服务':
                        intents.append(hotel_service)
                else:
                    intents.append(hotel_service)

        elif bot_id == str(Role.CAREANA.value):

            if health_intent:
                intents.append(health_intent)
                # 给睡眠报告添加报告时间
                if health_intent.get('intent') == str(Intent.REPORT.value):
                    health_intent['report_date'] = report_date

            if health_fix_qa and not health_intent:
                intents.append(health_fix_qa)

            if health_product:
                intents.append(health_product)

            if health_service:
                intents.append(health_service)

        # 合并数字人话术
        responses = [v.get('response') for v in intents if v.get('response') and v.get('response') != 'None']
        if len(responses) > 1:
            response = IntentService.merge_response(responses)
            # 将回复融合到第一个元素中
            for idx, intent in enumerate(intents):
                if idx == 0:
                    intent['response'] = response
                else:
                    intent['response'] = ''
        else:
            response = responses[0] if responses else ''

        # 工作人员回答
        if responses:
            IntentService.add_user_chat(user_id, response, type='answer')
            if chat:
                chat.bot_message = response
                chat.detail = {'intents': intents}

                session.commit()

        return intents

    @staticmethod
    async def get_model_parameter(hotel_services: list[Service] = None, hotel_questions: list[Question] = None, health_questions: list[Question] = None, health_products: list[Product] = None, health_services: list[Service] = None):

        ret_data = dict()
        if hotel_services:
            ret_data['hotel_services'] = IntentService.format_hotel_services(hotel_services)

        if hotel_questions:
            ret_data['hotel_questions'] = IntentService.format_questions(hotel_questions)

        if health_questions:
            ret_data['health_questions'] = IntentService.format_questions(health_questions)

        if health_products:
            ret_data['health_products'] = [v.dict() for v in health_products]

        if health_services:
            ret_data['health_services'] = [v.dict() for v in health_services]

        return ret_data



if __name__ == '__main__':

    services = [
        {
            "id": 1,
            "code": "W23350",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/toothbrush.png",
            "name": "外卖代取",
            "sub_title": "",
            "introduction": "外卖到后，此流程可以告知酒店工作人员，让机器人配送",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": []
        },
        {
            "id": 2,
            "code": "T23249",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/%E7%89%A9%E5%93%81%E9%85%8D%E9%80%81.png",
            "name": "物品配送",
            "sub_title": "",
            "introduction": "可以配送物品(如${items}等)",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "拖鞋"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "牙膏牙刷"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "梳子"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "剃须刀"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "香皂"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "毛巾\/浴巾"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "擦鞋布"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "浴帽"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "卫生纸\/抽纸"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "矿泉水"
                }
            ]
        },
        {
            "id": 3,
            "code": "B23248",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/park.png",
            "name": "叫车服务",
            "sub_title": "",
            "introduction": "",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": []
        },
        {
            "id": 4,
            "code": "Y23147",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/wifi%E4%BF%A1%E6%81%AF.png",
            "name": "WIFI信息",
            "sub_title": "",
            "introduction": "可以告诉客户WiFi如何连接",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": []
        },
        {
            "id": 5,
            "code": "I20743",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/pickup.png",
            "name": "接送机服务",
            "sub_title": "",
            "introduction": "酒店提供接送机服务，需要提前预约",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": 0.0,
                    "content": null,
                    "detail": [],
                    "title": "是"
                },
                {
                    "price": 0.0,
                    "content": null,
                    "detail": [],
                    "title": "否"
                }
            ]
        },
        {
            "id": 6,
            "code": "U20741",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/pillow.png",
            "name": "更换枕头",
            "sub_title": "",
            "introduction": "该服务可以为客户更换好点的枕头，柔软的枕头。(如${items}等)",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": 0.0,
                    "content": null,
                    "detail": [],
                    "title": "记忆棉枕"
                },
                {
                    "price": 0.0,
                    "content": null,
                    "detail": [],
                    "title": "颗粒枕"
                },
                {
                    "price": 0.0,
                    "content": null,
                    "detail": [],
                    "title": "乳胶枕"
                },
                {
                    "price": 0.0,
                    "content": null,
                    "detail": [],
                    "title": "羽绒枕"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "荞麦枕"
                }
            ]
        },
        {
            "id": 7,
            "code": "V20740",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/delay_checkout.png",
            "name": "延时退房\/续住",
            "sub_title": "",
            "introduction": "",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "续住"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "延时退房"
                }
            ]
        },
        {
            "id": 8,
            "code": "G20739",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/repairs.png",
            "name": "设备报修",
            "sub_title": "",
            "introduction": "可以报修设备(如${items}等)",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "吹风机"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "电灯"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "门锁"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "热水器"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "空调"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "熨烫机"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "电视机"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "保险箱"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "马桶"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "其他"
                }
            ]
        },
        {
            "id": 9,
            "code": "N20738",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/bill.png",
            "name": "开发票",
            "sub_title": "",
            "introduction": "",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "纸质发票"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "电子发票"
                }
            ]
        },
        {
            "id": 10,
            "code": "B20533",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/fitness.png",
            "name": "打包邮寄",
            "sub_title": "",
            "introduction": "行李打包邮件，上门取件，邮寄等服务",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": []
        },
        {
            "id": 11,
            "code": "K20527",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/play.png",
            "name": "礼品购物",
            "sub_title": "",
            "introduction": "酒店纪念品和当地特产纪念品等（中秋节礼物）(如${items}等)",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": 308.0,
                    "content": null,
                    "detail": [
                        {
                            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/question\/media\/cm-1724308456018-%E5%96%9C%E6%9C%882.jpg",
                            "content": "广式红豆沙月饼*2\n广式香芋月饼*2\n广式提拉米苏月饼*1\n广式椰蓉月饼*1\n广式香辣牛肉月饼*1\n广式蛋黄莲白莲蓉月饼*1"
                        }
                    ],
                    "title": "喜月礼盒"
                },
                {
                    "price": 399.0,
                    "content": null,
                    "detail": [
                        {
                            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/question\/media\/cm-1724308615328-%E8%8A%B1%E6%9C%881.jpg",
                            "content": "广式红豆沙月饼*1\n广式椰蓉月饼*1\n广式藤椒牛肉月饼*1\n广式绿豆蓉月饼（木糖醇）*1\n广式XO干贝月饼*1\n广式蛋黄白莲蓉月饼*1\n广式香芋月饼*1\n广式提拉米苏月饼*1"
                        }
                    ],
                    "title": "花月礼盒"
                },
                {
                    "price": 468.0,
                    "content": null,
                    "detail": [
                        {
                            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/question\/media\/cm-1724308748302-%E5%8D%8E%E6%9C%882.jpg",
                            "content": "桃山杨枝甘露月饼*1\n桃山小青柑普洱月饼*1\n广式红豆沙月饼*2\n广式香芋月饼*2\n广式椰蓉月饼*2\n广式绿豆蓉月饼*2\n广式提拉米苏月饼*2"
                        }
                    ],
                    "title": "华月礼盒"
                },
                {
                    "price": 499.0,
                    "content": null,
                    "detail": [
                        {
                            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/question\/media\/cm-1724308882969-%E7%9A%93%E6%9C%882.jpg",
                            "content": "广式红豆沙月饼*2\n广式浓情咖啡月饼*2\n广式香芋月饼*2\n广式绿豆蓉月饼*2\n广式提拉米苏月饼*2"
                        }
                    ],
                    "title": "皓月礼盒"
                },
                {
                    "price": 499.0,
                    "content": null,
                    "detail": [
                        {
                            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/question\/media\/cm-1724308987234-%E5%B0%8A%E6%9C%882.jpg",
                            "content": "桃山皮柠檬芝士月饼*1\n桃山芋泥芝士奇亚籽莲蓉月饼（木糖醇）*1\n桃山杨枝甘露月饼*1\n桃山小青柑普洱月饼*1\n广式五仁月饼*1\n广式XO干贝月饼*1\n广式蛋黄白莲蓉月饼*1\n广式红豆沙月饼*1\n广式香芋月饼*1\n广式绿豆蓉月饼*1\n广式黑芝麻月饼*2\n广式红酒蔓越莓月饼*1"
                        }
                    ],
                    "title": "尊月礼盒"
                }
            ]
        },
        {
            "id": 12,
            "code": "M20526",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/change_the_bedding.png",
            "name": "更换床上用品",
            "sub_title": "",
            "introduction": "更换床垫，床上四件套(如${items}等)",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "更换床垫"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "更换床上四件套"
                }
            ]
        },
        {
            "id": 13,
            "code": "I20525",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/other.png",
            "name": "场所预定",
            "sub_title": "",
            "introduction": "会议厅预订、多功能厅预订、宴会大厅，帮助客户需要开会，举办商业活动时使用",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "宴会厅"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "会议室"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "多功能厅 "
                }
            ]
        },
        {
            "id": 14,
            "code": "M20524",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/change_the_bedding.png",
            "name": "夜床服务",
            "sub_title": "",
            "introduction": "夜床服务是一种酒店提供的额外服务，旨在为客人提供更加舒适和宁静的夜间休息环境。通常包括整理床铺、提供夜间小食或饮品等。",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": []
        },
        {
            "id": 15,
            "code": "V20523",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/room_cleaning.png",
            "name": "房间清扫",
            "sub_title": "",
            "introduction": "预约房间清扫",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": []
        },
        {
            "id": 16,
            "code": "E20522",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/time.png",
            "name": "叫醒服务",
            "sub_title": "",
            "introduction": "客房座机\/上门叫醒，客户因有事情（会议，飞机，火车，高铁）需要指定时间提醒他，叫醒他可推此服务",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "客房电话叫醒"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "上门叫醒"
                }
            ]
        },
        {
            "id": 17,
            "code": "V20521",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/breakfast.png",
            "name": "送餐订餐",
            "sub_title": "",
            "introduction": "可以预定早餐，午餐，晚餐，夜宵等服务，商品有${items}等",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "不辣"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "微辣"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "中辣"
                }
            ]
        },
        {
            "id": 18,
            "code": "V20520",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/%E6%B4%97%E8%A1%A3%E6%9C%8D%E5%8A%A1.png",
            "name": "洗衣服务",
            "sub_title": "",
            "introduction": "自助洗衣，干洗服务",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "需要"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "不需要"
                }
            ]
        },
        {
            "id": 19,
            "code": "S20519",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/%E6%8E%A8%E6%8B%BF%E6%B2%BB%E7%96%97.png",
            "name": "水疗中心（SPA）",
            "sub_title": "",
            "introduction": "提供理疗和按摩服务(如${items}等)",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "腰部"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "头部"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "肩部"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "颈部"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "腿部"
                },
                {
                    "price": null,
                    "content": null,
                    "detail": [],
                    "title": "背部"
                }
            ]
        },
        {
            "id": 20,
            "code": "J20518",
            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/icon\/parents_child.png",
            "name": "健身中心",
            "sub_title": "",
            "introduction": "健身中心有${items}等",
            "detail_images": [],
            "price": 0,
            "simply_broadcast_text": "",
            "detailed_broadcast_text": "",
            "items": [
                {
                    "price": null,
                    "content": "我们的健身中心每周七天，每天24小时开放。酒店健身房供客人免费使用，需要请刷您的房卡进入。健身房提供有氧运动与阻力训练设备",
                    "detail": [
                        {
                            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/question\/media\/cm-1724059307398-%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_17240587671074.png",
                            "content": "我们的健身中心每周七天，每天24小时开放。健身房供客人免费使用，需要请刷您的房卡进入。"
                        }
                    ],
                    "title": "健身房"
                },
                {
                    "price": null,
                    "content": "室内恒温泳池每周七天开放，从早上6:30至晚上11:00，确保您在任何时间都能享受到游泳的乐趣。",
                    "detail": [
                        {
                            "image": "https:\/\/roboland-ai.oss-cn-shenzhen.aliyuncs.com\/assichat\/question\/media\/cm-1724059295943-sheraton-wuhan-hankou.jpg",
                            "content": "室内恒温泳池每周七天开放，从早上6:30至晚上11:00，确保您在任何时间都能享受到游泳的乐趣。"
                        }
                    ],
                    "title": "室内游泳池"
                }
            ]
        }
    ]

    print(IntentService.format_hotel_services(services))
